package com.sanri.buildingblock.exportcenter;

import com.sanri.buildingblock.exportcenter.annotation.ExcelExport;
import com.sanri.buildingblock.exportcenter.remote.TaskAdmin;
import com.sanri.buildingblock.exportcenter.remote.dto.TaskConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * @description:
 * @author: huangzhengri
 * @time: 2023-04-14 16:19
 */
@Component
public class HookMain implements ApplicationContextAware, SmartInitializingSingleton, DisposableBean {
    private Logger log = LoggerFactory.getLogger(HookMain.class);

    private static ApplicationContext applicationContext;

    @Override
    public void destroy() throws Exception {

    }

    @Override
    public void afterSingletonsInstantiated() {
        initExportConfig();
    }

    /**
     * 初始化加载配置
     */
    private void initExportConfig() {
        if (applicationContext == null) {
            return;
        }
        // init job handler from method
        String[] beanDefinitionNames = applicationContext.getBeanNamesForType(Object.class, false, true);
        for (String beanDefinitionName : beanDefinitionNames) {

            // get bean
            Object bean = null;
            Lazy onBean = applicationContext.findAnnotationOnBean(beanDefinitionName, Lazy.class);
            if (onBean != null) {
                log.debug("export center annotation scan, skip @Lazy Bean:{}", beanDefinitionName);
                continue;
            }
            bean = applicationContext.getBean(beanDefinitionName);

            // filter method
            Map<Method, ExcelExport> annotatedMethods = null;   // referred to ：org.springframework.context.event.EventListenerMethodProcessor.processBean
            try {
                annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(),
                        new MethodIntrospector.MetadataLookup<ExcelExport>() {
                            @Override
                            public ExcelExport inspect(Method method) {
                                return AnnotatedElementUtils.findMergedAnnotation(method, ExcelExport.class);
                            }
                        });
            } catch (Throwable ex) {
                log.error("export center  method- resolve error for bean[" + beanDefinitionName + "].", ex);
            }
            if (annotatedMethods == null || annotatedMethods.isEmpty()) {
                continue;
            }

            // generate and regist method
            for (Map.Entry<Method, ExcelExport> methodXxlJobEntry : annotatedMethods.entrySet()) {
                Method executeMethod = methodXxlJobEntry.getKey();
                ExcelExport excelExport = methodXxlJobEntry.getValue();
                registerTaskConfig(excelExport, executeMethod);
            }
        }
    }

    @Value("${spring.application.name:undefined}")
    private String applicationName;

    @Autowired
    private TaskAdmin taskAdmin;

    private void registerTaskConfig(ExcelExport excelExport, Method executeMethod) {
        TaskConfig taskConfig = new TaskConfig();
        Signature signature = ReflectUtils.getSignature(executeMethod);
        taskConfig.config(excelExport.uniqueId(),excelExport.description(),excelExport.type(),signature.toString(),applicationName,excelExport.sync());

        // 判断库中是否存在配置, 如果多服务同时注册, 使用最后成功的记录
        taskAdmin.register(taskConfig);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        HookMain.applicationContext = applicationContext;
    }
}
